#ifndef DATABASE_MANAGER_H
#define DATABASE_MANAGER_H
#include "../datamanager/Data.h"
#include "../datamanager/DataManager.h"
#include "../debugfunc/DebugFunc.h"
#include "../tablemanager/Table.h"
#include <string>
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <io.h>

struct TableColumn{
    TableColumn(std::string cn, std::string t, int l, bool in){
        column_name = cn;
        type = t;
        length = l;
        isNull = in;
    }
    std::string column_name;
    std::string type;
    int length;
    bool isNull;
};

class DatabaseManager{
public:
    DatabaseManager(){
        fm = new FileManager();
        bpm = new BufPageManager(fm);
        dm = new DataManager(fm,bpm);
        fileID = -1;
    }
    ~DatabaseManager(){
        if(fileID != -1){
            bpm->close();
            fm->closeFile(fileID);
        }
        delete fm;
        delete bpm;
        delete dm;
    }
private:
    DataManager* dm;
    FileManager* fm;
    BufPageManager* bpm;
    int fileID;
    string db_inuse;
public:
    DataManager* getDataManager() { return dm; }
    void createDatabase(std::string name){
        if((_access(("databases\\"+name).c_str(), 0 )) != -1 ){
            std::cout << "Error : Database has already exist"<< std::endl;
            return;
        }
        else if(!fm->createFile(("databases\\"+name).c_str())){
            system("mkdir databases");
            if(!fm->createFile(("databases\\"+name).c_str())){
                cout  << "Error : Fail to create database" << endl;
                return;
            }
        }
        int oldFileID = fileID;
        string oldName = db_inuse;
        int tempFileID;
        fm->openFile(("databases\\"+name).c_str(), tempFileID);
        dm->setFileID(tempFileID);
        dm->initDatabase();
        bpm->close();
        fm->closeFile(tempFileID);
        if(oldFileID != -1){
            fileID = -1;
            useDatabase(db_inuse);
        }
    }
    void dropDatabase(std::string name){
        if(name == db_inuse){
            bpm->close();
            fm->closeFile(fileID);
        }
        if(remove(("databases\\"+name).c_str()) == -1)
            std::cout << "Error : Fail to drop database " << name << std::endl;
        else if(name == db_inuse){
            fileID = -1;
        }
    }
    void useDatabase(std::string name){
        if(name == db_inuse && fileID != -1){
        }
        else{
            bpm->close();
            if((_access(("databases\\"+name).c_str(), 0 )) == -1 ){
                std::cout << "Error : Database does not exist"<< std::endl;
                fileID = -1;
            }
            else if(fm->openFile(("databases\\"+name).c_str(), fileID)){
                dm->setFileID(fileID);
                db_inuse = name;
            }
            else{
                std::cout << "Error : Fail to use database " << name << std::endl;
                fileID = -1;
            }
        }
    }

    void getFiles( std::string path, std::string exd, std::vector<std::string>& files )
    {
    	//文件句柄
    	long   hFile   =   0;
    	//文件信息
    	struct _finddata_t fileinfo;
    	std::string pathName, exdName;

    	if (0 != strcmp(exd.c_str(), ""))
    	{
    		exdName = "\\*." + exd;
    	}
    	else
    	{
    		exdName = "\\*";
    	}

    	if((hFile = _findfirst(pathName.assign(path).append(exdName).c_str(),&fileinfo)) !=  -1)
    	{
    		do
    		{
    			//如果是文件夹中仍有文件夹,迭代之
    			//如果不是,加入列表
    			if((fileinfo.attrib &  _A_SUBDIR))
    			{
    			}
    			else
    			{
    				if(strcmp(fileinfo.name,".") != 0  &&  strcmp(fileinfo.name,"..") != 0)
    					files.push_back(fileinfo.name);
    			}
    		}while(_findnext(hFile, &fileinfo)  == 0);
    		_findclose(hFile);
    	}
    }

    void showDatabases(){
        std::vector<std::string> files;
        getFiles("databases\\", "", files);
        for(int i = 0; i < files.size(); ++i)
            std::cout << files[i] << std::endl;
    }

    void showDatabase(){
        if(fileID == -1){
            std::cout << "Error : No database in use" << std::endl;
            return;
        }
        else{
        	vector<search_condition> condition;
            condition.clear();
        	search_result res;
        	dm->searchData(condition,SYS_OBJ,res);
        	printSearchResult(res);
        }
    }

    void showDatabase(std::string dbName){
        string inuse = db_inuse;
        useDatabase(dbName);
        showDatabase();
        useDatabase(inuse);
    }

    void createTable(std::string tableName, std::vector<TableColumn> tableColumns, string primary_key){
        if(fileID == -1){
            std::cout << "Error : No database in use" << std::endl;
            return;
        }
        else{
            vector<search_condition> condition;
            condition.clear();
            condition.push_back(search_condition(N_TABLE_NAME,tableName));
            search_result res;
            dm->searchData(condition, SYS_OBJ, res);
            if(res.size() != 0){
                cout << "Error : Table already exist!" << endl;
                return;
            }
            bool found = false;
            for(int i = 0; i < tableColumns.size(); ++i){
                if(tableColumns[i].column_name == primary_key){
                    found = true;
                    break;
                }
            }
            if(!found && primary_key != ""){
                std::cout << "Error : Unmatched primary key." << std::endl;
                return;
            }
            Table newTable(dm);
            newTable.setName(tableName);
            for(int i = 0; i < tableColumns.size(); ++i){
                newTable.addColumn(
                    tableColumns[i].column_name,
                    tableColumns[i].type,
                    tableColumns[i].length,
                    tableColumns[i].isNull);
            }
            for(int i = 0; i < tableColumns.size(); ++i){
                if(tableColumns[i].column_name == primary_key){
                    newTable.setPrimaryKey(i+1);
                    break;
                }
            }
            newTable.create();
        }
    }

    void getTableColumns(std::string tableName, search_result& res){
        if(fileID == -1){
            std::cout << "Error : No database in use" << std::endl;
            return;
        }
        else{
            vector<search_condition> condition;
            condition.clear();
            condition.push_back(search_condition(N_TABLE_NAME,tableName));
            res.clear();
            dm->searchData(condition, SYS_OBJ, res);
            if(res.size() == 0){
                cout << "Error : Table not found!" << endl;
                return;
            }
            string RID = res[0][N_RID];
            condition.clear();
            condition.push_back(search_condition(N_INDEX_ID,RID));
            dm->searchData(condition, SYS_COLUMN, res);
            if(res.size() == 0){
                cout << "Error : Table in system is ruined." << endl;
                return;
            }
        }
    }

    void showTable(std::string tableName){
        if(fileID == -1){
            std::cout << "Error : No database in use" << std::endl;
            return;
        }
        else{
            vector<search_condition> condition;
            condition.clear();
            condition.push_back(search_condition(N_TABLE_NAME,tableName));
            search_result res;
            dm->searchData(condition, SYS_OBJ, res);
            if(res.size() == 0){
                cout << "Error : Table not found!" << endl;
                return;
            }
            string RID = res[0][N_RID];
            condition.clear();
            condition.push_back(search_condition(N_COLUMN_ID,RID));
            dm->searchData(condition, SYS_COLUMN, res);
            if(res.size() == 0){
                cout << "Error : Table in system is ruined." << endl;
                return;
            }
            else{
                std::cout << "RID | ";
                std::cout << "ID | ";
                std::cout << "NAME | ";
                std::cout << "TYPE | ";
                std::cout << "LENGTH | ";
                std::cout << "RANK | ";
                std::cout << "NULL | ";
                std::cout << std::endl;
        	    printSearchResult(res, 1);
            }
        }
    }

    void dropTable(std::string tableName){
        if(fileID == -1){
            std::cout << "Error : No database in use" << std::endl;
            return;
        }
        else{
            vector<search_condition> condition;
            condition.clear();
            condition.push_back(search_condition(N_TABLE_NAME,tableName));
            search_result res;
            dm->searchData(condition, SYS_OBJ, res);
            if(res.size() == 0){
                cout << "Error : Table not found!" << endl;
                return;
            }
            dm->deleteData(condition, SYS_OBJ);
            string RID = res[0][N_RID];
            condition.clear();
            condition.push_back(search_condition(N_COLUMN_ID,RID));
            dm->deleteData(condition, SYS_COLUMN);
            condition.clear();
            condition.push_back(search_condition(N_INDEX_ID,RID));
            dm->deleteData(condition, SYS_INDEX);
        }
    }
};

#endif // DATABASE_MANAGER_H
